
const { ccclass, property, executeInEditMode } = cc._decorator;

function deleteFromDynamicAtlas(comp, frame) {
    if (frame && !CC_TEST) {
        if (frame._original && cc.dynamicAtlasManager) {
            //@ts-ignore
            cc.dynamicAtlasManager.deleteAtlasSpriteFrame(frame);
            frame._resetDynamicAtlasFrame();
        }
    }
}

function getFontFamily(comp) {
    if (!comp.useSystemFont) {
        if (comp.font) {
            if (comp.font._nativeAsset) {
                return comp.font._nativeAsset;
            }
            cc.assetManager.postLoadNative(comp.font, function (err) {
                comp.isValid && comp.setVertsDirty();
            });
            return 'Arial';
        }

        return 'Arial';
    }
    else {
        return comp.fontFamily || 'Arial';
    }
}

let AssemblerClass = null;

const _invisibleAlpha = (1 / 255).toFixed(3);
const MAX_SIZE = 2048;

let _context = null;
let _canvas = null;
let _texture = null;

let _fontDesc = '';
let _string = '';
let _fontSize = 0;
let _drawFontSize = 0;
let _splitedStrings = [];
let _canvasSize = cc.Size.ZERO;
let _lineHeight = 0;
let _hAlign = 0;
let _vAlign = 0;
let _color = null;
let _fontFamily = '';
let _overflow = cc.Label.Overflow.NONE;
let _isWrapText = false;
let _premultiply = false;

// outline
let _outlineComp = null;
let _outlineColor = cc.Color.WHITE;

// shadow
let _shadowComp = null;
let _shadowColor = cc.Color.BLACK;

let _canvasPadding = cc.rect();
let _contentSizeExtend = cc.Size.ZERO;
let _nodeContentSize = cc.Size.ZERO;

let _enableBold = false;
let _enableItalic = false;
let _enableUnderline = false;
let _underlineThickness = 0;

let _drawUnderlinePos = cc.Vec2.ZERO;
let _drawUnderlineWidth = 0;

let _sharedLabelData;

let _ellipsis = false

const Alignment = [
    'left', // macro.TextAlignment.LEFT
    'center', // macro.TextAlignment.CENTER
    'right' // macro.TextAlignment.RIGHT
];

function createAssembler() {
    if (AssemblerClass) {
        return new AssemblerClass();
    }
    //@ts-ignore
    let WebglTTFAssembler = cc.Label.__assembler__?.TTF;
    if (!WebglTTFAssembler) {
        return null;
    }
    //@ts-ignore
    class TrumpTextAssembler extends cc.Assembler2D {

        _letterPadding = 10
        _justify = true

        _getAssemblerData() {
            //@ts-ignore
            _sharedLabelData = cc.Label._canvasPool.get();
            _sharedLabelData.canvas.width = _sharedLabelData.canvas.height = 1;
            return _sharedLabelData;
        }

        _resetAssemblerData(assemblerData) {
            if (assemblerData) {
                //@ts-ignore
                cc.Label._canvasPool.put(assemblerData);
            }
        }

        updateRenderData(comp) {
            super.updateRenderData(comp);

            if (!comp._vertsDirty) return;
            this._letterPadding = comp.padding || 0
            this._updateProperties(comp);
            this._calculateLabelFont();
            this._updateLabelDimensions();
            this._updateTexture();
            this._calDynamicAtlas(comp);

            comp._actualFontSize = _fontSize;
            comp.node.setContentSize(_nodeContentSize);

            this.updateVerts(comp);

            comp._vertsDirty = false;

            _context = null;
            _canvas = null;
            _texture = null;
        }

        updateUVs(comp) {
            //@ts-ignore
            let verts = this._renderData.vDatas[0];
            let uv = comp._frame.uv;
            //@ts-ignore
            let uvOffset = this.uvOffset;
            //@ts-ignore
            let floatsPerVert = this.floatsPerVert;
            for (let i = 0; i < 4; i++) {
                let srcOffset = i * 2;
                let dstOffset = floatsPerVert * i + uvOffset;
                verts[dstOffset] = uv[srcOffset];
                verts[dstOffset + 1] = uv[srcOffset + 1];
            }
        }

        updateColor(comp) {
            //@ts-ignore
            cc.Color.WHITE._fastSetA(comp.node._color.a);
            //@ts-ignore
            let color = cc.Color.WHITE._val;

            super.updateColor(comp, color);
        }

        updateVerts(comp) {
            let node = comp.node,
                canvasWidth = comp._ttfTexture.width,
                canvasHeight = comp._ttfTexture.height,
                appx = node.anchorX * node.width,
                appy = node.anchorY * node.height;

            let shadow = cc.LabelShadow && comp.getComponent(cc.LabelShadow);
            if (shadow && shadow._enabled) {
                // adapt size changed caused by shadow
                let offsetX = (canvasWidth - node.width) / 2;
                let offsetY = (canvasHeight - node.height) / 2;

                let shadowOffset = shadow.offset;
                if (-shadowOffset.x > offsetX) {
                    // expand to left
                    appx += (canvasWidth - node.width);
                }
                else if (offsetX > shadowOffset.x) {
                    // expand to left and right
                    appx += (offsetX - shadowOffset.x);
                }
                else {
                    // expand to right, no need to change render position
                }

                if (-shadowOffset.y > offsetY) {
                    // expand to top
                    appy += (canvasHeight - node.height);
                }
                else if (offsetY > shadowOffset.y) {
                    // expand to top and bottom
                    appy += (offsetY - shadowOffset.y);
                }
                else {
                    // expand to bottom, no need to change render position
                }
            }

            //@ts-ignore
            let local = this._local;
            local[0] = -appx;
            local[1] = -appy;
            local[2] = canvasWidth - appx;
            local[3] = canvasHeight - appy;

            this.updateUVs(comp);
            //@ts-ignore
            this.updateWorldVerts(comp);
        }

        _updatePaddingRect() {
            let top = 0, bottom = 0, left = 0, right = 0;
            let outlineWidth = 0;
            _contentSizeExtend.width = _contentSizeExtend.height = 0;
            if (_outlineComp) {
                outlineWidth = _outlineComp.width;
                top = bottom = left = right = outlineWidth;
                _contentSizeExtend.width = _contentSizeExtend.height = outlineWidth * 2;
            }
            if (_shadowComp) {
                let shadowWidth = _shadowComp.blur + outlineWidth;
                left = Math.max(left, -_shadowComp._offset.x + shadowWidth);
                right = Math.max(right, _shadowComp._offset.x + shadowWidth);
                top = Math.max(top, _shadowComp._offset.y + shadowWidth);
                bottom = Math.max(bottom, -_shadowComp._offset.y + shadowWidth);
            }
            if (_enableItalic) {
                //0.0174532925 = 3.141592653 / 180
                let offset = _drawFontSize * Math.tan(12 * 0.0174532925);
                right += offset;
                _contentSizeExtend.width += offset;
            }
            _canvasPadding.x = left;
            _canvasPadding.y = top;
            _canvasPadding.width = left + right;
            _canvasPadding.height = top + bottom;
        }

        _updateProperties(comp) {
            let assemblerData = comp._assemblerData;
            if(assemblerData == null)
                return
            _context = assemblerData.context;
            _canvas = assemblerData.canvas;
            _texture = comp._frame._original ? comp._frame._original._texture : comp._frame._texture;

            _string = comp.string.toString();
            _fontSize = comp._fontSize;
            _drawFontSize = _fontSize;
            _underlineThickness = comp.underlineHeight || _drawFontSize / 8;
            _overflow = comp.overflow;
            _canvasSize.width = comp.node.width;
            _canvasSize.height = comp.node.height;
            _nodeContentSize = comp.node.getContentSize();
            _lineHeight = comp._lineHeight;
            _hAlign = comp.horizontalAlign;
            _vAlign = comp.verticalAlign;
            _color = comp.node.color;
            _enableBold = comp.enableBold;
            _enableItalic = comp.enableItalic;
            _enableUnderline = comp.enableUnderline;
            _fontFamily = getFontFamily(comp);
            _premultiply = comp.srcBlendFactor === cc.macro.BlendFactor.ONE;

            _ellipsis = comp.ellipsis
            this._justify = comp._justify || false
            // cc.log("Label Context class:", cc.js.getClassName(_context))
            //@ts-ignore
            if (CC_NATIVERENDERER) {
                _context._setPremultiply(_premultiply);
            }

            if (_overflow === cc.Label.Overflow.NONE) {
                _isWrapText = false;
            }
            else if (_overflow === cc.Label.Overflow.RESIZE_HEIGHT) {
                _isWrapText = true;
            }
            else {
                _isWrapText = comp.enableWrapText;
            }

            // outline
            _outlineComp = cc.LabelOutline && comp.getComponent(cc.LabelOutline);
            _outlineComp = (_outlineComp && _outlineComp.enabled && _outlineComp.width > 0) ? _outlineComp : null;
            if (_outlineComp) {
                _outlineColor.set(_outlineComp.color);
            }

            // shadow
            _shadowComp = cc.LabelShadow && comp.getComponent(cc.LabelShadow);
            _shadowComp = (_shadowComp && _shadowComp.enabled) ? _shadowComp : null;
            if (_shadowComp) {
                _shadowColor.set(_shadowComp.color);
                // TODO: temporary solution, cascade opacity for outline color
                _shadowColor.a = _shadowColor.a * comp.node.color.a / 255.0;
            }

            this._updatePaddingRect();
        }

        _calculateFillTextStartPosition() {
            let labelX = 0;
            if (_hAlign === cc.macro.TextAlignment.RIGHT) {
                labelX = _canvasSize.width - _canvasPadding.width;
            } else if (_hAlign === cc.macro.TextAlignment.CENTER) {
                labelX = (_canvasSize.width - _canvasPadding.width) / 2;
            }

            let lineHeight = this._getLineHeight();
            let drawStartY = lineHeight * (_splitedStrings.length - 1);
            // TOP
            //@ts-ignore
            let firstLinelabelY = _fontSize * (1 - cc.textUtils.BASELINE_RATIO / 2);

            //@ts-ignore
            if (_vAlign !== cc.macro.VerticalTextAlignment.TOP) {
                // free space in vertical direction
                let blank = drawStartY + _canvasPadding.height + _fontSize - _canvasSize.height;
                //@ts-ignore
                if (_vAlign === cc.macro.VerticalTextAlignment.BOTTOM) {
                    // Unlike BMFont, needs to reserve space below.
                    //@ts-ignore
                    blank += cc.textUtils.BASELINE_RATIO / 2 * _fontSize;
                    // BOTTOM
                    firstLinelabelY -= blank;
                } else {
                    // CENTER
                    firstLinelabelY -= blank / 2;
                }
            }
            //@ts-ignore
            firstLinelabelY += cc.textUtils.BASELINE_OFFSET * _fontSize;

            return cc.v2(labelX + _canvasPadding.x, firstLinelabelY + _canvasPadding.y);
        }

        _setupOutline() {
            _context.strokeStyle = `rgba(${_outlineColor.r}, ${_outlineColor.g}, ${_outlineColor.b}, ${_outlineColor.a / 255})`;
            _context.lineWidth = _outlineComp.width * 2;
        }

        _setupShadow() {
            _context.shadowColor = `rgba(${_shadowColor.r}, ${_shadowColor.g}, ${_shadowColor.b}, ${_shadowColor.a / 255})`;
            _context.shadowBlur = _shadowComp.blur;
            _context.shadowOffsetX = _shadowComp.offset.x;
            _context.shadowOffsetY = -_shadowComp.offset.y;
        }

        private _drawOneLineStr(startPosition, lineHeight, string: string, i) {
            let drawTextPosX = startPosition.x;
            let drawTextPosY = startPosition.y + i * lineHeight;
            let isMultiple = _splitedStrings.length > 1 && _shadowComp;
            let measureText = this._measureText(_context, _fontDesc);
            if (isMultiple) {
                if (_outlineComp) {
                    _context.strokeText(string, drawTextPosX, drawTextPosY);
                }

                _context.fillText(string, drawTextPosX, drawTextPosY);
            }

            // draw underline
            if (_enableUnderline) {
                _drawUnderlineWidth = measureText(string);
                if (_hAlign === cc.macro.TextAlignment.RIGHT) {
                    _drawUnderlinePos.x = startPosition.x - _drawUnderlineWidth;
                } else if (_hAlign === cc.macro.TextAlignment.CENTER) {
                    _drawUnderlinePos.x = startPosition.x - (_drawUnderlineWidth / 2);
                } else {
                    _drawUnderlinePos.x = startPosition.x;
                }
                _drawUnderlinePos.y = drawTextPosY + _drawFontSize / 8;
                _context.fillRect(_drawUnderlinePos.x, _drawUnderlinePos.y, _drawUnderlineWidth, _underlineThickness);
            }
        }

        _drawTextEffect(startPosition, lineHeight) {
            if (!_shadowComp && !_outlineComp && !_enableUnderline) return;

            let isMultiple = _splitedStrings.length > 1 && _shadowComp;

            // only one set shadow and outline
            if (_shadowComp) {
                this._setupShadow();
            }

            if (_outlineComp) {
                this._setupOutline();
            }

            // draw shadow and (outline or text)
            for (let i = 0; i < _splitedStrings.length; ++i) {
                // multiple lines need to be drawn outline and fill text
                this._drawOneLineStr(startPosition, lineHeight, _splitedStrings[i], i)
            }

            if (isMultiple) {
                _context.shadowColor = 'transparent';
            }
        }

        private _updateTextureOneline(string: string, drawTextPosX, drawTextPosY) {
            if (this._letterPadding == 0 && this._justify == false) {
                if (_outlineComp) {
                    _context.strokeText(string, drawTextPosX, drawTextPosY);
                }
                _context.fillText(string, drawTextPosX, drawTextPosY);
                return
            }
            //@ts-ignore
            string = string.trimRight()
            //计算justify间隔
            let spaceCnt = string.split(" ").length - 1;
            let textWidth = this._measureTextWidth(string)
            let widthLeft = _nodeContentSize.width - textWidth
            let wordsAdd = 0
            if (widthLeft > 0 && spaceCnt > 0) {
                wordsAdd = Math.floor(widthLeft / spaceCnt)
            }
            let x = drawTextPosX
            for (let len = 0; len < string.length; ++len) {
                //@ts-ignore
                let paraLength = cc.textUtils.safeMeasureText(_context, string[len], _fontDesc)
                if (_outlineComp) {
                    _context.strokeText(string, x, drawTextPosY);
                }
                _context.fillText(string[len], x, drawTextPosY);
                if (string[len] == " " && wordsAdd > 0) {
                    x += wordsAdd
                }
                x += paraLength + this._letterPadding
            }
        }

        _updateTexture() {
            _context.clearRect(0, 0, _canvas.width, _canvas.height);
            // use round for line join to avoid sharp intersect point
            _context.lineJoin = 'round';
            //Add a white background to avoid black edges.
            if (!_premultiply) {
                //TODO: it is best to add alphaTest to filter out the background color.
                let _fillColor = _outlineComp ? _outlineColor : _color;
                _context.fillStyle = `rgba(${_fillColor.r}, ${_fillColor.g}, ${_fillColor.b}, ${_invisibleAlpha})`;
                _context.fillRect(0, 0, _canvas.width, _canvas.height);
                _context.fillStyle = `rgba(${_color.r}, ${_color.g}, ${_color.b}, 1)`;
            } else {
                _context.fillStyle = `rgba(${_color.r}, ${_color.g}, ${_color.b}, ${_color.a / 255.0})`;
            }

            let startPosition = this._calculateFillTextStartPosition();
            let lineHeight = this._getLineHeight();
            let drawTextPosX = startPosition.x, drawTextPosY = 0;
            // draw shadow and underline
            this._drawTextEffect(startPosition, lineHeight);
            // draw text and outline
            for (let i = 0; i < _splitedStrings.length; ++i) {
                drawTextPosY = startPosition.y + i * lineHeight;
                // if (_outlineComp) {
                //     _context.strokeText(_splitedStrings[i], drawTextPosX, drawTextPosY);
                // }
                // _context.fillText(_splitedStrings[i], drawTextPosX, drawTextPosY);
                this._updateTextureOneline(_splitedStrings[i], drawTextPosX, drawTextPosY)
            }

            if (_shadowComp) {
                _context.shadowColor = 'transparent';
            }

            _texture.handleLoadedTexture();
        }

        _calDynamicAtlas(comp) {
            if (comp.cacheMode !== cc.Label.CacheMode.BITMAP) return;
            let frame = comp._frame;
            // Delete cache in atlas.
            deleteFromDynamicAtlas(comp, frame);
            if (!frame._original) {
                frame.setRect(cc.rect(0, 0, _canvas.width, _canvas.height));
            }
            //@ts-ignore
            this.packToDynamicAtlas(comp, frame);
        }

        _updateLabelDimensions() {
            //@ts-ignore
            let maxTextureSize = cc.renderer.device.caps ? cc.renderer.device.caps.maxTextureSize : MAX_SIZE;
            if (_canvasSize.width > maxTextureSize || _canvasSize.height > maxTextureSize) {
                cc.warn("The maximum texture size supported by the device is " + maxTextureSize);
            }
            _canvasSize.width = Math.min(_canvasSize.width, maxTextureSize);
            _canvasSize.height = Math.min(_canvasSize.height, maxTextureSize);

            let recreate = false;
            if (_canvas.width !== _canvasSize.width) {
                _canvas.width = _canvasSize.width;
                recreate = true
            }

            if (_canvas.height !== _canvasSize.height) {
                _canvas.height = _canvasSize.height;
                recreate = true
            }

            recreate && (_context.font = _fontDesc);
            // align
            _context.textAlign = Alignment[_hAlign];
        }

        _getFontDesc() {
            let fontDesc = _fontSize.toString() + 'px ';
            fontDesc = fontDesc + _fontFamily;
            if (_enableBold) {
                fontDesc = "bold " + fontDesc;
            }
            if (_enableItalic) {
                fontDesc = "italic " + fontDesc;
            }
            return fontDesc;
        }

        _getLineHeight() {
            let nodeSpacingY = _lineHeight;
            if (nodeSpacingY === 0) {
                nodeSpacingY = _fontSize;
            } else {
                nodeSpacingY = nodeSpacingY * _fontSize / _drawFontSize;
            }

            return nodeSpacingY | 0;
        }

        _calculateParagraphLength(paragraphedStrings, ctx) {
            let paragraphLength = [];

            for (let i = 0; i < paragraphedStrings.length; ++i) {
                //@ts-ignore
                let width = cc.textUtils.safeMeasureText(ctx, paragraphedStrings[i], _fontDesc);
                paragraphLength.push(width);
            }

            return paragraphLength;
        }

        _measureText(ctx, fontDesc) {
            let _letterPadding = this._letterPadding
            return function (string) {
                //@ts-ignore
                let w1 = cc.textUtils.safeMeasureText(ctx, string, fontDesc)
                let paddingAll = (string.length >= 1 ? string.length - 1 : 0) * _letterPadding
                return w1 + paddingAll;
            };
        }

        _calculateShrinkFont(paragraphedStrings) {
            let paragraphLength = this._calculateParagraphLength(paragraphedStrings, _context);

            let i = 0;
            let totalHeight = 0;
            let maxLength = 0;

            if (_isWrapText) {
                let canvasWidthNoMargin = _nodeContentSize.width;
                let canvasHeightNoMargin = _nodeContentSize.height;
                if (canvasWidthNoMargin < 0 || canvasHeightNoMargin < 0) {
                    return;
                }
                totalHeight = canvasHeightNoMargin + 1;
                let actualFontSize = _fontSize + 1;
                let textFragment = "";
                //let startShrinkFontSize = actualFontSize | 0;
                let left = 0, right = actualFontSize | 0, mid = 0;

                while (left < right) {
                    mid = (left + right + 1) >> 1;

                    if (mid <= 0) {
                        //@ts-ignore
                        cc.logID(4003);
                        break;
                    }

                    _fontSize = mid;
                    _fontDesc = this._getFontDesc();
                    _context.font = _fontDesc;
                    let lineHeight = this._getLineHeight();

                    totalHeight = 0;
                    for (i = 0; i < paragraphedStrings.length; ++i) {
                        //@ts-ignore
                        let allWidth = cc.textUtils.safeMeasureText(_context, paragraphedStrings[i], _fontDesc);
                        //@ts-ignore
                        textFragment = cc.textUtils.fragmentText(paragraphedStrings[i],
                            allWidth,
                            canvasWidthNoMargin,
                            this._measureText(_context, _fontDesc));
                        totalHeight += textFragment.length * lineHeight;
                    }

                    if (totalHeight > canvasHeightNoMargin) {
                        right = mid - 1;
                    } else {
                        left = mid;
                    }
                }

                if (left === 0) {
                    //@ts-ignore
                    cc.logID(4003);
                } else {
                    _fontSize = left;
                    _fontDesc = this._getFontDesc();
                    _context.font = _fontDesc;
                }
            } else {
                totalHeight = paragraphedStrings.length * this._getLineHeight();

                for (i = 0; i < paragraphedStrings.length; ++i) {
                    if (maxLength < paragraphLength[i]) {
                        maxLength = paragraphLength[i];
                    }
                }
                let scaleX = (_canvasSize.width - _canvasPadding.width) / maxLength;
                let scaleY = _canvasSize.height / totalHeight;

                _fontSize = (_drawFontSize * Math.min(1, scaleX, scaleY)) | 0;
                _fontDesc = this._getFontDesc();
                _context.font = _fontDesc;
            }
        }

        //添加的
        private _measureTextWidth(text: string) {
            //@ts-ignore
            return cc.textUtils.safeMeasureText(_context, text, _fontDesc) + (text.length >= 1 ? text.length - 1 : 0) * this._letterPadding;
        }

        _calculateWrapText(paragraphedStrings) {
            if (!_isWrapText) return;

            _splitedStrings = [];
            let canvasWidthNoMargin = _nodeContentSize.width;
            for (let i = 0; i < paragraphedStrings.length; ++i) {
                let allWidth = this._measureTextWidth(paragraphedStrings[i])
                //@ts-ignore
                let textFragment = this.fragmentText(paragraphedStrings[i],
                    allWidth,
                    canvasWidthNoMargin);
                _splitedStrings = _splitedStrings.concat(textFragment);
                // cc.log(`allwidth:${allWidth}, fragment:${textFragment.toString()}, canvasWidthNoMargin:${canvasWidthNoMargin}`)
            }
            // cc.log("_splitedStrings:", _splitedStrings.toString())
        }

        private _calculateEllipsis(paragraphedStrings) {
            if (_isWrapText) return; //要换行的不处理
            if (paragraphedStrings.length != 1) return
            _splitedStrings = [];

            let str: string = paragraphedStrings[0]
            let allWidth = this._measureTextWidth(str)
            let canvasWidthNoMargin = _nodeContentSize.width;
            if (allWidth <= canvasWidthNoMargin) {
                _splitedStrings.push(str)
                return
            }
            while (allWidth > canvasWidthNoMargin) {
                str = str.slice(0, -1)
                if (str.length == 0)
                    break
                allWidth = this._measureTextWidth(str + "...")
            }
            _splitedStrings.push(str + "...")
            cc.log("省略后:", str)
        }

        _safeSubstring(targetString, startIndex, endIndex = undefined) {
            let newStartIndex = startIndex, newEndIndex = endIndex;
            let startChar = targetString[startIndex];
            //@ts-ignore
            if (cc.textUtils.lowSurrogateRex.test(startChar)) {
                newStartIndex--;
            }
            if (endIndex !== undefined) {
                if (endIndex - 1 !== startIndex) {
                    let endChar = targetString[endIndex - 1];
                    //@ts-ignore
                    if (cc.textUtils.highSurrogateRex.test(endChar)) {
                        newEndIndex--;
                    }
                }
                //@ts-ignore
                else if (cc.textUtils.highSurrogateRex.test(startChar)) {
                    newEndIndex++;
                }
            }
            return targetString.substring(newStartIndex, newEndIndex);
        }

        fragmentText(stringToken, allWidth, maxWidth) {
            var wrappedWords = [];
            //fast return if strArr is empty
            if (stringToken.length === 0 || maxWidth < 0) {
                wrappedWords.push('');
                return wrappedWords;
            }
            var text = stringToken;
            while (allWidth > maxWidth && text.length > 1) {

                var fuzzyLen = text.length * (maxWidth / allWidth) | 0;
                var tmpText = this._safeSubstring(text, fuzzyLen);
                var width = allWidth - this._measureTextWidth(tmpText);
                var sLine = tmpText;
                var pushNum = 0;

                var checkWhile = 0;
                var checkCount = 10;

                //Exceeded the size
                while (width > maxWidth && checkWhile++ < checkCount) {
                    fuzzyLen *= maxWidth / width;
                    fuzzyLen = fuzzyLen | 0;
                    tmpText = this._safeSubstring(text, fuzzyLen);
                    width = allWidth - this._measureTextWidth(tmpText);
                }

                checkWhile = 0;

                //Find the truncation point
                while (width <= maxWidth && checkWhile++ < checkCount) {
                    if (tmpText) {
                        //@ts-ignore
                        var exec = cc.textUtils.label_wordRex.exec(tmpText);
                        pushNum = exec ? exec[0].length : 1;
                        sLine = tmpText;
                    }

                    fuzzyLen = fuzzyLen + pushNum;
                    tmpText = this._safeSubstring(text, fuzzyLen);
                    width = allWidth - this._measureTextWidth(tmpText);
                }

                fuzzyLen -= pushNum;
                // in case maxWidth cannot contain any characters, need at least one character per line
                if (fuzzyLen === 0) {
                    fuzzyLen = 1;
                    sLine = this._safeSubstring(text, 1);
                }
                //@ts-ignore
                else if (fuzzyLen === 1 && cc.textUtils.highSurrogateRex.test(text[0])) {
                    fuzzyLen = 2;
                    sLine = this._safeSubstring(text, 2);
                }

                var sText = this._safeSubstring(text, 0, fuzzyLen), result;

                //symbol in the first
                if (true) {
                    //@ts-ignore
                    if (cc.textUtils.label_symbolRex.test(sLine || tmpText)) {
                        //@ts-ignore
                        result = cc.textUtils.label_lastWordRex.exec(sText);
                        fuzzyLen -= result ? result[0].length : 0;
                        if (fuzzyLen === 0) fuzzyLen = 1;

                        sLine = this._safeSubstring(text, fuzzyLen);
                        sText = this._safeSubstring(text, 0, fuzzyLen);
                    }
                }

                //To judge whether a English words are truncated
                //@ts-ignore
                if (cc.textUtils.label_firstEnglish.test(sLine)) {
                    //@ts-ignore
                    result = cc.textUtils.label_lastEnglish.exec(sText);
                    if (result && sText !== result[0]) {
                        fuzzyLen -= result[0].length;
                        sLine = this._safeSubstring(text, fuzzyLen);
                        sText = this._safeSubstring(text, 0, fuzzyLen);
                    }
                }

                // The first line And do not wrap should not remove the space
                if (wrappedWords.length === 0) {
                    wrappedWords.push(sText);
                }
                else {
                    sText = sText.trimLeft();
                    if (sText.length > 0) {
                        wrappedWords.push(sText);
                    }
                }
                text = sLine || tmpText;
                allWidth = this._measureTextWidth(text);
            }

            if (wrappedWords.length === 0) {
                wrappedWords.push(text);
            }
            else {
                text = text.trimLeft();
                if (text.length > 0) {
                    wrappedWords.push(text);
                }
            }
            return wrappedWords;
        }

        _calculateLabelFont() {
            let paragraphedStrings = _string.split('\n');

            _splitedStrings = paragraphedStrings;
            _fontDesc = this._getFontDesc();
            _context.font = _fontDesc;

            switch (_overflow) {
                case cc.Label.Overflow.NONE: {
                    //这里完成算画布大小
                    let canvasSizeX = 0;
                    let canvasSizeY = 0;
                    for (let i = 0; i < paragraphedStrings.length; ++i) {
                        let str = paragraphedStrings[i]
                        //@ts-ignore
                        let paraLength = this._measureTextWidth(str)
                        canvasSizeX = canvasSizeX > paraLength ? canvasSizeX : paraLength;
                    }
                    //@ts-ignore
                    canvasSizeY = (_splitedStrings.length + cc.textUtils.BASELINE_RATIO) * this._getLineHeight();
                    let rawWidth = parseFloat(canvasSizeX.toFixed(2));
                    let rawHeight = parseFloat(canvasSizeY.toFixed(2));
                    _canvasSize.width = rawWidth + _canvasPadding.width;
                    _canvasSize.height = rawHeight + _canvasPadding.height;
                    _nodeContentSize.width = rawWidth + _contentSizeExtend.width;
                    _nodeContentSize.height = rawHeight + _contentSizeExtend.height;
                    break;
                }
                case cc.Label.Overflow.SHRINK: {
                    this._calculateShrinkFont(paragraphedStrings);
                    this._calculateWrapText(paragraphedStrings);
                    break;
                }
                case cc.Label.Overflow.CLAMP: {
                    this._calculateEllipsis(paragraphedStrings)
                    this._calculateWrapText(paragraphedStrings);
                    break;
                }
                case cc.Label.Overflow.RESIZE_HEIGHT: {
                    this._calculateWrapText(paragraphedStrings);
                    //@ts-ignore
                    let rawHeight = (_splitedStrings.length + cc.textUtils.BASELINE_RATIO) * this._getLineHeight();
                    _canvasSize.height = rawHeight + _canvasPadding.height;
                    // set node height
                    _nodeContentSize.height = rawHeight + _contentSizeExtend.height;
                    break;
                }
            }
        }
    }

    AssemblerClass = TrumpTextAssembler;
    return new AssemblerClass();
}

@ccclass
@executeInEditMode
export default class TrumpLabel extends cc.Label {
    @property()
    _padding = 0;
    @property()
    get padding() {
        return this._padding
    }
    set padding(v) {
        this._padding = v
        //@ts-ignore
        this.setVertsDirty();
    }

    @property()
    _justify = false
    @property
    get justify() {
        return this._justify
    }
    set justify(v) {
        this._justify = v
        //@ts-ignore
        this.setVertsDirty();
    }

    @property()
    _ellipsis = false;
    @property()
    get ellipsis() {
        return this._ellipsis
    }
    set ellipsis(v) {
        if (v != this._ellipsis) {
            this._ellipsis = v
            //@ts-ignore
            this.setVertsDirty();
        }
    }

    protected _resetAssembler() {
        let assembler = createAssembler();
        if (!assembler) {
            //@ts-ignore
            super._resetAssembler();
            return;
        }
        //@ts-ignore
        this._assembler = assembler;
        //@ts-ignore
        this.setVertsDirty();
        assembler.init(this);
        //@ts-ignore
        // super._resetAssembler();
    }
}